package jee.boot.module.act.service;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.collect.Maps;
import jee.boot.common.basic.CrudService;
import jee.boot.common.basic.ServiceException;
import jee.boot.common.basic.result.MapData;
import jee.boot.common.utils.JsonMapper;
import jee.boot.entity.activiti.ActModel;
import jee.boot.module.act.dao.ActModelDao;
import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.image.ProcessDiagramGenerator;
import org.activiti.image.impl.DefaultProcessDiagramGenerator;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 模型服务
 *
 * @author zhilei.wang
 */
@Service
public class ActModelService extends CrudService<ActModelDao, ActModel> {
    @Autowired
    private RepositoryService repositoryService;

    @Override
    public void save(ActModel entity) {
        try {
            // 保存模型数据
            org.activiti.engine.repository.Model modelData = repositoryService.newModel();
            modelData.setKey(entity.getKey());
            modelData.setName(entity.getName());
            modelData.setCategory(entity.getCategory());
            modelData.setVersion(Integer.parseInt(String.valueOf(repositoryService.createModelQuery().modelKey(modelData.getKey()).count() + 1)));
            modelData.setMetaInfo(entity.getMetaInfo());
            repositoryService.saveModel(modelData);
            // 初始化流程图画布
            Map<String, Object> editorNode = Maps.newHashMap();
            editorNode.put("id", "canvas");
            editorNode.put("resourceId", "canvas");
            Map<String, Object> stencilset = Maps.newHashMap();
            stencilset.put("namespace", "http://b3mn.org/stencilset/bpmn2.0#");
            editorNode.put("stencilset", stencilset);
            repositoryService.addModelEditorSource(modelData.getId(), JsonMapper.toJsonString(editorNode).getBytes(StandardCharsets.UTF_8));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 更新
     *
     * @param entity
     */
    @Override
    public void update(ActModel entity) {
        org.activiti.engine.repository.Model modelData = repositoryService.getModel(entity.getId());
        modelData.setKey(entity.getKey());
        modelData.setName(entity.getName());
        modelData.setCategory(entity.getCategory());
        modelData.setMetaInfo(entity.getMetaInfo());
        repositoryService.saveModel(modelData);
    }

    /**
     * 删除
     *
     * @param entity
     */
    @Override
    public void delete(ActModel entity) {
        repositoryService.deleteModel(entity.getId());
    }

    /**
     * 从模型中部署
     *
     * @param entity
     */
    public void deploy(ActModel entity) {
        try {
            org.activiti.engine.repository.Model modelData = repositoryService.getModel(entity.getId());
            BpmnJsonConverter jsonConverter = new BpmnJsonConverter();
            JsonNode editorNode = new ObjectMapper().readTree(repositoryService.getModelEditorSource(modelData.getId()));
            BpmnModel bpmnModel = jsonConverter.convertToBpmnModel(editorNode);
            BpmnXMLConverter xmlConverter = new BpmnXMLConverter();
            byte[] bpmnBytes = xmlConverter.convertToXML(bpmnModel);
            String processName = modelData.getName();
            // 实例名称必须以.bpmn20.xml结尾,否则部署失败
            if (!StringUtils.endsWith(processName, ".bpmn20.xml")) {
                processName += ".bpmn20.xml";
            }
            ByteArrayInputStream in = new ByteArrayInputStream(bpmnBytes);
            Deployment deployment = repositoryService.createDeployment().name(modelData.getName()).category(modelData.getCategory()).addInputStream(processName, in).deploy();
            List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery().deploymentId(deployment.getId()).list();
            for (ProcessDefinition processDefinition : list) {
                repositoryService.setProcessDefinitionCategory(processDefinition.getId(), modelData.getCategory());
            }
            if (list.isEmpty()) {
                throw new ServiceException(1408);
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw new ServiceException(1440, MapData.instance().put("modelId", entity.getId()).data());
        }
    }

    /**
     * 导出model的xml文件
     */
    public void exportXml(ActModel entity, HttpServletResponse response) {
        try {
            org.activiti.engine.repository.Model modelData = repositoryService.getModel(entity.getId());
            BpmnJsonConverter jsonConverter = new BpmnJsonConverter();
            JsonNode editorNode = new ObjectMapper().readTree(repositoryService.getModelEditorSource(modelData.getId()));
            BpmnModel bpmnModel = jsonConverter.convertToBpmnModel(editorNode);
            BpmnXMLConverter xmlConverter = new BpmnXMLConverter();
            byte[] bpmnBytes = xmlConverter.convertToXML(bpmnModel);
            ByteArrayInputStream in = new ByteArrayInputStream(bpmnBytes);
            IOUtils.copy(in, response.getOutputStream());
            String filename = bpmnModel.getMainProcess().getId() + ".bpmn20.xml";
            response.setContentType("application/octet-stream; charset=utf-8");
            response.setHeader("Content-Disposition", "attachment; filename=" + filename);
            response.flushBuffer();
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw new ServiceException(1441, MapData.instance().put("modelId", entity.getId()).data());
        }

    }

    /**
     * 导出model的xml文件
     */
    public void exportPng(ActModel entity, HttpServletResponse response) {
        try {
            org.activiti.engine.repository.Model modelData = repositoryService.getModel(entity.getId());
            ObjectNode modelNode = (ObjectNode) new ObjectMapper().readTree(repositoryService.getModelEditorSource(modelData.getId()));
            BpmnModel model = new BpmnJsonConverter().convertToBpmnModel(modelNode);
            ProcessDiagramGenerator processDiagramGenerator = new DefaultProcessDiagramGenerator();
            InputStream resourceAsStream = processDiagramGenerator.generateDiagram(model, "png", new ArrayList<String>(), new ArrayList<String>(), "宋体", "宋体", "宋体", null, 1.0);
            //读取输入流
            int count = resourceAsStream.available();
            byte[] bc = new byte[count];
            resourceAsStream.read(bc);
            ByteArrayInputStream in = new ByteArrayInputStream(bc);
            response.setContentType("image/png");
            IOUtils.copy(in, response.getOutputStream());
            response.flushBuffer();
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw new ServiceException(1442, MapData.instance().put("modelId", entity.getId()).data());
        }

    }


}
